package ast

import (
	"fmt"
	"showen/num"
	"showen/tok"
	"strings"
)

type ExpType int

const (
	BOOLEAN_EXP ExpType = iota
	NUMBER_EXP
	CHARACTER_EXP
	STRING_EXP
	IDENTIFIER_EXP
	LIST_EXP
	QUOTE_EXP
	QUASIQUOTE_EXP
	UNQUOTE_EXP
	UNQUOTE_SPLICING_EXP
	VECTOR_EXP
)

type Exp interface {
	Type() ExpType
	String() string
}

type BooleanExp struct {
	Token tok.Token
}

func (b *BooleanExp) Type() ExpType {
	return BOOLEAN_EXP
}

func (b *BooleanExp) String() string {
	return b.Token.Lexeme
}

type IdentifierExp struct {
	Token tok.Token
}

func (s *IdentifierExp) Type() ExpType {
	return IDENTIFIER_EXP
}
func (s *IdentifierExp) String() string {
	return s.Token.Lexeme
}

type NumberExp struct {
	Token tok.Token
}

func (i *NumberExp) Type() ExpType {
	return NUMBER_EXP
}

func (i *NumberExp) String() string {
	return i.Token.Literal.(num.Number).String()
}

type StringExp struct {
	Token tok.Token
}

func (s *StringExp) Type() ExpType {
	return STRING_EXP
}
func (s *StringExp) String() string {
	return fmt.Sprintf("\"%s\"", s.Token.Literal)
}

type ListExp struct {
	Exps []Exp
}

func (l *ListExp) Type() ExpType {
	return LIST_EXP
}

func (l *ListExp) String() string {
	var out strings.Builder
	out.WriteString("(")
	for i, exp := range l.Exps {
		if i != 0 {
			out.WriteString(" ")
		}
		out.WriteString(exp.String())
	}
	out.WriteString(")")
	return out.String()
}

type QuoteExp struct {
	Token tok.Token // the quote token
	Value Exp       // the quoted expression
}

func (qe *QuoteExp) String() string {
	var out strings.Builder
	out.WriteString("'")
	out.WriteString(qe.Value.String())
	return out.String()
}

func (qe *QuoteExp) Type() ExpType {
	return QUOTE_EXP
}

type QuasiquoteExp struct {
	Token tok.Token // the quasiquote token
	Value Exp       // the quasiquoted expression
}

func (qe *QuasiquoteExp) String() string {
	var out strings.Builder
	out.WriteString("`")
	out.WriteString(qe.Value.String())
	return out.String()
}

func (qe *QuasiquoteExp) Type() ExpType {
	return QUASIQUOTE_EXP
}

type UnquoteExp struct {
	Token tok.Token // the unquote token
	Value Exp       // the unquoted expression
}

func (ue *UnquoteExp) String() string {
	var out strings.Builder
	out.WriteString(",")
	out.WriteString(ue.Value.String())
	return out.String()
}

func (ue *UnquoteExp) Type() ExpType {
	return UNQUOTE_EXP
}

type UnquoteSplicingExp struct {
	Token tok.Token // the unquote-splicing token
	Value Exp       // the unquote-spliced expression
}

func (use *UnquoteSplicingExp) String() string {
	var out strings.Builder
	out.WriteString(",@")
	out.WriteString(use.Value.String())
	return out.String()
}

func (use *UnquoteSplicingExp) Type() ExpType {
	return UNQUOTE_SPLICING_EXP
}

type CharacterExp struct {
	Token tok.Token
}

func (ce *CharacterExp) Type() ExpType {
	return CHARACTER_EXP
}
func (ce *CharacterExp) String() string {
	// Handle special characters
	switch ce.Token.Literal {
	case ' ':
		return "#\\space"
	case '\n':
		return "#\\newline"
	case '\t':
		return "#\\tab"
	default:
		return fmt.Sprintf("#\\%c", ce.Token.Literal)
	}
}

type VectorExp struct {
	Exps []Exp // the elements of the vector
}

func (ve *VectorExp) Type() ExpType {
	return VECTOR_EXP
}
func (ve *VectorExp) String() string {
	var elements []string
	for _, exp := range ve.Exps {
		elements = append(elements, exp.String())
	}
	return fmt.Sprintf("#(%s)", strings.Join(elements, " "))
}
